package top.jandmla.excel.util.shpIntoDB;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.geotools.data.DataStore;
import org.geotools.data.DataStoreFinder;
import org.geotools.data.FeatureWriter;
import org.geotools.data.Transaction;
import org.geotools.data.postgis.PostgisNGDataStoreFactory;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.feature.AttributeTypeBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.feature.type.GeometryTypeImpl;
import org.geotools.geometry.jts.JTS;
import org.geotools.jdbc.JDBCDataStore;
import org.geotools.referencing.CRS;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.locationtech.jts.geom.Geometry;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.feature.type.GeometryDescriptor;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
import top.jandmla.excel.pojo.User;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ImportShpTools {

    /**
     * 用于上传shape图层坐标系
     */
    public static CoordinateReferenceSystem coordinateReferenceSystem = null;

    /**
     * 获取数据库链接
     *
     * @param user 数据库链接信息
     * @return 数据库连接对象
     */
    public static JDBCDataStore getDataStore(User user) {

        //数据库连接参数配置
        Map<String, Object> map = new HashMap<>();
        map.put(PostgisNGDataStoreFactory.DBTYPE.key, user.getDbType());
        map.put(PostgisNGDataStoreFactory.HOST.key, user.getDbHost());
        map.put(PostgisNGDataStoreFactory.PORT.key, user.getDbPort());
        map.put(PostgisNGDataStoreFactory.DATABASE.key, user.getDataBase());
        map.put(PostgisNGDataStoreFactory.USER.key, user.getUsername());
        map.put(PostgisNGDataStoreFactory.PASSWD.key, user.getPassword());

        JDBCDataStore ds = null;
        DataStore _dataStore;

        try {
            _dataStore = DataStoreFinder.getDataStore(map);
            ds = (JDBCDataStore) _dataStore;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return ds;
    }

    /**
     * 按文件地址读取文件信息
     *
     * @param filePath 文件地址
     * @return 读取的信息列表
     */
    public static List<SimpleFeatureSource> readSHP(String filePath) {
        List<File> files = new ArrayList<>(1);
        files.add(FileUtils.getFile(filePath));
        return readSHP(files);
    }

    /**
     * 文件列表读取文件信息
     *
     * @param list 文件列表
     * @return 读取文件信息
     */
    public static List<SimpleFeatureSource> readSHP(List<File> list) {
        List<SimpleFeatureSource> result = new ArrayList<>(1);
        ShapefileDataStore shpDataStore = null;
        try {
            File[] fils = new File[list.size()];
            list.toArray(fils);
            URL[] urls = FileUtils.toURLs(fils);
            for (URL url : urls) {
                shpDataStore = new ShapefileDataStore(url);
                Charset charset = StandardCharsets.UTF_8;
                shpDataStore.setCharset(charset);
                String tableName = shpDataStore.getTypeNames()[0];
                SimpleFeatureSource featureSource = shpDataStore.getFeatureSource(tableName);
                result.add(featureSource);
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (shpDataStore != null)
                shpDataStore.dispose();
        }
        return result;
    }

    /**
     * 建表
     *
     * @param ds            数据库链接对象
     * @param featureSource 文件信息
     * @return 数据库链接对象
     */
    public static JDBCDataStore createTable(JDBCDataStore ds, SimpleFeatureSource featureSource, String spatialName) {

        SimpleFeatureType schema = featureSource.getSchema();
        try {
            String[] allTableNames = ds.getTypeNames();

            //如果存在,则先删除
            //这里根据需求决定是否删除表
            if (ArrayUtils.contains(allTableNames, schema.getTypeName())) {
                ds.removeSchema(schema.getTypeName());
                //防止shp文件名大写的问题
            } else if (ArrayUtils.contains(allTableNames, schema.getTypeName().toLowerCase())) {
                ds.removeSchema(schema.getTypeName().toLowerCase());
            }

            //由于此类属性内部不可变,所以需要获取旧属性,重新赋值给新建属性.
            //获取旧属性
            List<AttributeDescriptor> oldAttributeDescriptors = schema.getAttributeDescriptors();
            //新属性
            List<AttributeDescriptor> newAttributeDescriptors = new ArrayList<>();

            //新建feature构造器
            SimpleFeatureTypeBuilder simpleFeatureTypeBuilder = new SimpleFeatureTypeBuilder();
            //设置表名
            simpleFeatureTypeBuilder.setName(schema.getName().toString().toLowerCase());
            //设置坐标系
            simpleFeatureTypeBuilder.setCRS(schema.getCoordinateReferenceSystem());

            //获取属性
            for (AttributeDescriptor oldAttributeDescriptor : oldAttributeDescriptors) {

                //属性构造器
                AttributeTypeBuilder build = new AttributeTypeBuilder();
                build.init(oldAttributeDescriptor.getType());
                build.setNillable(true);

                //获取字段名,改为小写
                String name = StringUtils.isNotEmpty(oldAttributeDescriptor.getLocalName()) ? oldAttributeDescriptor.getLocalName().toLowerCase() : oldAttributeDescriptor.getLocalName();
                if (oldAttributeDescriptor instanceof GeometryDescriptor) {

                    //修改空间字段名
                    name = StringUtils.isNotEmpty(spatialName) ? spatialName : "shape";
                    GeometryTypeImpl geometryDescriptor = (GeometryTypeImpl) oldAttributeDescriptor.getType();

                    //获取坐标系,用于坐标系转换
                    coordinateReferenceSystem = geometryDescriptor.getCoordinateReferenceSystem();
                }

                //设置字段名
                build.setName(name);

                //创建新的属性类
                AttributeDescriptor descriptor = build.buildDescriptor(name, oldAttributeDescriptor.getType());
                newAttributeDescriptors.add(descriptor);
            }

            //使用新的属性类
            simpleFeatureTypeBuilder.addAll(newAttributeDescriptors);

            //获取新属性值                                                                                                                                                                                                                                                                                                                                                                                                                            
            schema = simpleFeatureTypeBuilder.buildFeatureType();
            //创建数据表
            ds.createSchema(schema);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return ds;
    }

    //shp数据写入数据库
    public static void writeShp2DataBin(JDBCDataStore ds, SimpleFeatureSource featureSource) {

        SimpleFeatureType schema = featureSource.getSchema();
        FeatureWriter<SimpleFeatureType, SimpleFeature> writer = null;
        SimpleFeatureIterator features = null;
        //开始写入数据
        try {
            writer = ds.getFeatureWriter(schema.getTypeName().toLowerCase(), Transaction.AUTO_COMMIT);
            SimpleFeatureCollection featureCollection = featureSource.getFeatures();
            features = featureCollection.features();

            CoordinateReferenceSystem crs = coordinateReferenceSystem;

            //默认转为wgs84
            CoordinateReferenceSystem worldCRS = DefaultGeographicCRS.WGS84;

            boolean lenient = true; // allow for some error due to different datums
            //定义坐标转换
            MathTransform transform = CRS.findMathTransform(crs, worldCRS, lenient);

            while (features.hasNext()) {
                writer.hasNext();
                SimpleFeature next = writer.next();
                SimpleFeature feature = features.next();
                for (int i = 0; i < feature.getAttributeCount(); i++) {
                    next.setAttribute(i, feature.getAttribute(i));
                }

                //坐标系转换
                Geometry geometry = (Geometry) feature.getDefaultGeometry();
                Geometry geometry2 = JTS.transform(geometry, transform);
                next.setDefaultGeometry(geometry2);

                //写入数据库
                writer.write();

            }
        } catch (IOException | TransformException | FactoryException e) {
            e.printStackTrace();
        } finally {
            if (writer != null) {
                IOUtils.closeQuietly(writer);
            }
            if (features != null) {
                features.close();
            }
            if (ds != null) {
                ds.dispose();
            }

        }
    }
}

